package com.smart.business.service;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.smart.business.dao.LocalDictDao;
import com.smart.business.dao.MeterTypeDao;
import com.smart.business.dao.ParamDao;
import com.smart.common.utils.ThreadLocalUtil;
import com.smart.entity.business.*;
import com.smart.entity.system.DictEntity;
import com.smart.model.exception.SmartException;
import com.smart.mybatis.service.impl.BaseServiceImpl;
import com.smart.business.dao.PointDao;
import com.smart.service.business.PointService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 点位配置 ServiceImpl
 *
 * @author wf
 * @since 2025-07-14 10:43:13
 */
@Service("pointService")
@Transactional(rollbackFor = Exception.class)
public class pointServiceImpl extends BaseServiceImpl<PointDao, PointEntity> implements PointService {
    @Autowired
    private PointDao pointDao;
    @Autowired
    private MeterTypeDao meterTypeDao;
    @Autowired
    private DynamicTableServiceImpl dynamicTable;
    @Autowired
    private LocalDictDao localDictDao;
    @Autowired
    private ParamDao paramDao;

    @Override
    public List<PointEntity> getPointList(String id, String meterTypeId, String name, String code, int current, int size) {
        // 判断是否有参数传入
        boolean hasParams = Arrays.stream(new String[]{id, meterTypeId, name, code})
                .anyMatch(StringUtils::isNotBlank);

        try (Page<List<PointEntity>> ignored = PageHelper.startPage(current, size)) {
            if (hasParams) {
                // 有参数，按参数查询
                //log.error("执行根据查询");
                List<PointEntity> pointList = pointDao.getPointList(id, meterTypeId, name, code);
                pointList.forEach(point -> {
                    point.setMeterTypeId(meterTypeDao.selectById(point.getMeterTypeId()).getName());
                });
                return pointList;

            } else {
                // 没有参数，使用权限范围内的公司名称进行查询
                List<String> permissionResult = ThreadLocalUtil.getPermissionResult();
                if (permissionResult == null || permissionResult.isEmpty()) {
                    throw new RuntimeException("权限数据未初始化，请检查 AOP 是否生效");
                }
                // 查询权限范围内的点位数据
                //log.error("执行根据权限范围内公司名称查询");
                List<PointEntity> pointList = pointDao.getListByCompanyNames(permissionResult);
                pointList.forEach(point -> {
                    point.setMeterTypeId(meterTypeDao.selectById(point.getMeterTypeId()).getName());
                });
                return pointList;

            }
        }
    }

    @Override
    public List<CollectorEntity> unConnectCollector() {
        List<String> companyName = ThreadLocalUtil.getPermissionResult();
        if (companyName == null || companyName.isEmpty()) {
            throw new SmartException("权限数据未初始化，请检查 AOP 是否生效");
        }

        // 获取所有采集器实体
        List<CollectorEntity> allCollectors = pointDao.allCollector(companyName);


        // 提取采集器ID列表
        List<String> allCollectorIds = allCollectors.stream()
                .map(CollectorEntity::getId)
                .collect(Collectors.toList());
        if (allCollectors.isEmpty()) {
            throw new SmartException("该公司下没有所属采集器！");
        }

        // 查询每个采集器已使用的通道数
        List<UsedChannelVO> usedChannelsList = pointDao.countUsedChannels(allCollectorIds);

        // 转换为 Map<collectorId, usedChannels>
        Map<String, Integer> usedChannelsMap = usedChannelsList.stream()
                .collect(Collectors.toMap(
                        UsedChannelVO::getCollectorId,
                        UsedChannelVO::getUsedChannels
                ));

        // 过滤出未满动态通道的采集器实体
        return allCollectors.stream()
                .filter(collector -> {
                    Integer used = usedChannelsMap.getOrDefault(collector.getId(), 0);
                    return used < collector.getChannelCount();
                })
                .collect(Collectors.toList());

//        // 过滤出未满动态通道的采集器实体
//        return allCollectors.stream()
//                .filter(collector -> {
//                    // 在 usedChannelsList 中查找匹配的记录
//                    return usedChannelsList.stream()
//                            .filter(usedChannel -> collector.getId().equals(usedChannel.getCollectorId()))
//                            .map(UsedChannelVO::getUsedChannels)
//                            .findFirst()
//                            .orElse(0) < collector.getChannelCount();
//                })
//                .collect(Collectors.toList());


    }


    @Override
    public PointEntity savePoint(PointEntity pointEntity) {
        //验证公司名是否正确
        //从aop中拿到公司名
        List<String> permissionResult = ThreadLocalUtil.getPermissionResult();
        String companyName = pointEntity.getCompanyName();
        if (!permissionResult.contains(companyName)) {
            throw new SmartException("请选择正确的公司");
        }
        pointEntity.setMeterTypeId(meterTypeDao.selectByName(pointEntity.getMeterTypeId()));
        if (pointEntity.getMeterTypeId() == null) {
            throw new SmartException("请选择正确的类型");
        }
        //判断点位id是否存在与数据库中
        if (pointDao.selectById(pointEntity.getId()) != null) {
            throw new SmartException("点位已存在");
        }
        pointDao.insert(pointEntity);
        return pointEntity;

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public PointEntity setConnectStatus(ConnectDTO connectDTO) {
        validateConnectDTO(connectDTO);

        String pointId = connectDTO.getPointId();
        String collectorId = connectDTO.getCollectorId();
        Map<String, String> parameterChannelMap = connectDTO.getParameterChannelMap();

        List<PointParameterEntity> parameterList = convertToParameterEntities(pointId, parameterChannelMap);
        pointDao.connectBatch(parameterList);
        pointDao.updateCollector(collectorId, pointId);
        updateCompanyDataTableFields(pointId, connectDTO.getTemplateId());

        return this.get(pointId);
    }

    private void validateConnectDTO(ConnectDTO connectDTO) {
        if (connectDTO == null) {
            throw new SmartException("连接参数不能为空");
        }

        if (StringUtils.isBlank(connectDTO.getPointId())) {
            throw new SmartException("点位 ID 不能为空");
        }

        if (StringUtils.isBlank(connectDTO.getCollectorId())) {
            throw new SmartException("采集器 ID 不能为空");
        }

        if (connectDTO.getParameterChannelMap() == null || connectDTO.getParameterChannelMap().isEmpty()) {
            throw new SmartException("参数映射不能为空");
        }
    }

    private List<PointParameterEntity> convertToParameterEntities(String pointId, Map<String, String> parameterChannelMap) {
        return parameterChannelMap.entrySet().stream()
                .map(entry -> {
                    PointParameterEntity param = new PointParameterEntity();
                    param.setPointId(pointId);
                    param.setChannelId(entry.getValue());
                    param.setParameterDefinitionId(entry.getKey());
                    return param;
                })
                .collect(Collectors.toList());
    }

    private void updateCompanyDataTableFields(String pointId, String templateId) {
        String tableName = localDictDao.getDictCode(pointId);
        if (tableName == null) {
            throw new SmartException("点位数据不存在");
        }

        List<ParamEntity> paramList = paramDao.templateParam(templateId);
        if (paramList == null) {
            throw new SmartException("模板数据不存在");
        }
        dynamicTable.addColumnsInBatch(tableName, paramList);
    }


    @Override
    public List<ChannelEntity> getUnConnectChannels(String collectorId) {
        return pointDao.getUnConnectChannels(collectorId);
    }

    @Override
    public String disConnect(String id) {
        PointEntity pointEntity = pointDao.selectById(id);
        if (pointEntity != null && pointEntity.getCollectorId() != null) {
            pointDao.setDisconnect(id);
            pointDao.deleteConnect(id);
            pointDao.recordCollector(pointEntity.getCollectorId(), pointEntity.getName(), id, pointEntity.getCompanyName());
            return id;
        }
        return null;
    }

    @Override
    public boolean deletePoints(List<String> deleteIds) {
        for (String id : deleteIds) {
            PointEntity point = pointDao.selectById(id);
            if (StringUtils.equals(point.getStatus(), "1")) {
                //抛出异常
                throw new SmartException("点位已连接，不能删除");
            }
        }
        pointDao.deletePoints(deleteIds);
        return true;
    }
    @Override
    public Map<String, Object> getList(String pointId, String startTime, String endTime, String frequency, int current, int size) {
        // 1. 查询点位信息，判断属于哪家公司
        if (StringUtils.isBlank(pointId)) {
            throw new SmartException("点位 ID 不能为空");
        }
        // 2. 查询该公司对应的 dict_code（即表名）
        String tableName = localDictDao.getTableNameByPointId(pointId);
        int offset = (current - 1) * size;

        // 4. 根据查询类型获取数据
        List<Map<String, Object>> result;
        int total;
        // 可添加frequency格式校验
        if (StringUtils.isNotBlank(frequency) && !Arrays.asList("hour", "day", "month").contains(frequency)) {
            throw new SmartException("不支持的频率类型");
        }
        List<String> pointIds;
        List<DictEntity> bottomLevelPoints = localDictDao.getBottomLevelPoints(pointId);
        if (bottomLevelPoints != null && !bottomLevelPoints.isEmpty()) {
            pointIds = bottomLevelPoints.stream()
                    .map(DictEntity::getId)
                    .collect(Collectors.toList());
        } else {
            pointIds = Collections.singletonList(pointId);
        }

        if (!StringUtils.isBlank(frequency)) {
            result = pointDao.getListByType(pointIds, startTime, endTime, frequency);
            total = result.size();
        } else {
            result = pointDao.getList(pointId, tableName, startTime, endTime, offset, size);
            total = pointDao.countList(pointId, startTime, endTime, tableName);
        }
        // 根据表名获取点位的计量单位
        String unit = pointDao.getUnit(tableName);

        // 5. 处理点位名称替换
        processPointName(result);
        List<Map<String, Object>> resultList = new ArrayList<>(result);

        // 6. 构建返回结果
        return buildResult( resultList, unit, total, tableName, current, size);
    }

    private void processPointName(List<Map<String, Object>> result) {
        if (result != null && !result.isEmpty()) {
            result.forEach(data -> {
                Object pidObj = data.get("point_id");
                if (pidObj != null) {
                    String pid = pidObj.toString();
                    PointEntity pe = pointDao.selectById(pid);
                    if (pe != null) {
                        data.put("point_id", pe.getName());
                    }
                }
            });
        }
    }

    private Map<String, Object> buildResult(List<Map<String, Object>> list, String unit, int total, String tableName,
                                            int current, int size) {

        // 按point_id分组
        Map<String, List<Map<String, Object>>> groupedData = list.stream()
                .collect(Collectors.groupingBy(map -> (String) map.get("point_id")));

        Map<String, Object> res = new LinkedHashMap<>();
        res.put("list", groupedData);
        res.put("unit", unit);
        res.put("total", total);
        res.put("tableName", tableName);
        res.put("current", current);
        res.put("size", size);
        res.put("pages", (long) Math.ceil((double) total / size));
        return res;
    }


    @Override
    public List<Map<String, Object>> getPointMessage() {
        List<String> companyNames = ThreadLocalUtil.getPermissionResult();
        if (companyNames == null || companyNames.isEmpty()) {
            return Collections.emptyList();
        }
        return pointDao.getPointMessages(companyNames);
    }

    @Override
    public List<Map<String, Object>> getBarChartData(String meterTypeId, String startTime, String endTime) {
        List<String> companyNames = ThreadLocalUtil.getPermissionResult();
        if (companyNames == null || companyNames.isEmpty()) {
            //抛出异常
            throw new SmartException("没有权限访问该公司数据");
        }
//        // 如果权限中只有一家公司，直接使用该公司的数据
//        List<String> filteredCompanies = companyNames;
//        if (companyName != null && !companyName.isEmpty()) {
//            // 如果指定了公司名称，则只查询该公司的数据
//            filteredCompanies = companyNames.stream()
//                    .filter(name -> name.equals(companyName))
//                    .collect(Collectors.toList());
//        }
//        // 如果过滤后没有公司，返回空列表
//        if (filteredCompanies.isEmpty()) {
//            //抛出异常
//            throw new SmartException("没有该公司");
//        }
        if (meterTypeId == null || meterTypeId.isEmpty()) {
            // 抛出异常
            throw new SmartException("未找到该能源类型");
        }
        // 根据能源类型和公司名称获取点位数据
        return pointDao.getBarChartData(meterTypeId, companyNames, startTime, endTime);
    }

    @Override
    public Map<String, Object> getTimeConsumption(String meterTypeId) {
        List<String> companyName = ThreadLocalUtil.getPermissionResult();
        if (companyName == null || companyName.isEmpty()) {
            //抛出异常
            throw new SmartException("没有权限访问该公司数据");
        }
        Map<String, Object> result = new HashMap<>();

        // 获取当前时间
        LocalDateTime now = LocalDateTime.now();

        // 计算当天开始和结束时间
        LocalDateTime startOfDay = now.withHour(0).withMinute(0).withSecond(0).withNano(0);
        LocalDateTime endOfDay = startOfDay.plusDays(1).minusNanos(1);
        String dayStart = startOfDay.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        String dayEnd = endOfDay.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

        // 计算当周开始和结束时间 (周一为一周开始)
        LocalDateTime startOfWeek = now.minusDays(now.getDayOfWeek().getValue() - 1)
                .withHour(0).withMinute(0).withSecond(0).withNano(0);
        LocalDateTime endOfWeek = startOfWeek.plusWeeks(1).minusNanos(1);
        String weekStart = startOfWeek.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        String weekEnd = endOfWeek.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

        // 计算当月开始和结束时间
        LocalDateTime startOfMonth = now.withDayOfMonth(1)
                .withHour(0).withMinute(0).withSecond(0).withNano(0);
        LocalDateTime endOfMonth = startOfMonth.plusMonths(1).minusNanos(1);
        String monthStart = startOfMonth.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        String monthEnd = endOfMonth.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

        // 获取当天消耗值
        List<Map<String, Object>> dayConsumptionList = pointDao.getCompanyDayConsumption(companyName,meterTypeId , dayStart, dayEnd);
        double dayConsumption = dayConsumptionList.stream()
                .mapToDouble(m -> Double.parseDouble(m.get("consumption").toString()))
                .sum();
        result.put("dayConsumption", dayConsumption);

        // 获取当周消耗值
        List<Map<String, Object>> weekConsumptionList = pointDao.getCompanyWeekConsumption(companyName,meterTypeId , weekStart, weekEnd);
        double weekConsumption = weekConsumptionList.stream()
                .mapToDouble(m -> Double.parseDouble(m.get("consumption").toString()))
                .sum();
        result.put("weekConsumption", weekConsumption);

        // 获取当月消耗值
        List<Map<String, Object>> monthConsumptionList = pointDao.getCompanyMonthConsumption(companyName,meterTypeId , monthStart, monthEnd);
        double monthConsumption = monthConsumptionList.stream()
                .mapToDouble(m -> Double.parseDouble(m.get("consumption").toString()))
                .sum();
        result.put("monthConsumption", monthConsumption);

        return result;

    }

    @Override
    public List<Map<String, Object>> getPieChartData() {
        List<String> companyName = ThreadLocalUtil.getPermissionResult();
        if (companyName == null || companyName.isEmpty()) {
            //抛出异常
            throw new SmartException("没有权限访问该公司数据");
        }
        // 根据公司名称获取各能源类型消耗值
        List<Map<String, Object>> rawData = pointDao.getPieChartData(companyName);
        //如果只有一种能源类型，则展示下一层级的点位消耗值对比
        if (rawData.size() == 1) {
           //获取能源ID
            String meterTypeId = rawData.get(0).get("meter_type_id").toString();
            return getSubLevelConsumptionData(companyName, meterTypeId);
        }
        //计算总消耗量
        double totalConsumption = rawData.stream()
                .mapToDouble(data -> ((Number) data.get("total_consumption")).doubleValue())
                .sum();

        // 转换为百分比形式
        List<Map<String, Object>> result = new ArrayList<>();
        for (Map<String, Object> data : rawData) {
            Map<String, Object> newData = new HashMap<>(data);

            double consumption = ((Number) data.get("total_consumption")).doubleValue();
            double percentage = totalConsumption > 0 ? (consumption / totalConsumption) * 100 : 0;

            newData.put("percentage", Math.round(percentage * 100) / 100 + "%"); // 保留两位小数
            newData.put("total_consumption", consumption); // 保持原始消耗值

            result.add(newData);
        }

        return result;
    }

    @Override
    public List<Map<String,Object>> getCompanyMeterType() {
        List<String> companyNames = ThreadLocalUtil.getPermissionResult();
        if (companyNames == null || companyNames.isEmpty()) {
            //抛出异常
            throw new SmartException("没有权限访问该公司数据");
        }
        return pointDao.getCompanyMeterType(companyNames);
    }

    @Override
    public PointEntity setDisable(String pointId, int isDisable) {
        PointEntity pointEntity = pointDao.selectById(pointId);
        if (pointEntity == null) {
            throw new SmartException("点位不存在");
        }
        // 如果状态未改变，直接返回
        if (pointEntity.getIsDisable() == isDisable) {
            return pointEntity;
        }
        // 更新点位状态
        pointEntity.setIsDisable(isDisable);
        int result = pointDao.updateById(pointEntity);

        if (result > 0) {
            // 同步更新相关字典表状态（如果需要）
            if (isDisable == 1) {
                // 启用时，同步更新字典表
                localDictDao.updateDictIsDisable(pointId, 0);
            } else {
                // 禁用时，同步更新字典表 软删除
                localDictDao.updateDictIsDisable(pointId, 1);
            }
            return pointEntity;
        } else {
            throw new SmartException("更新失败");
        }
    }




    /**
     * 获取下一层级的消耗数据（当只有一种能源类型时调用）
     * @param companyName 公司名称列表
     * @param meterTypeId 能源类型ID
     * @return 下一层级消耗数据
     */
    private List<Map<String, Object>> getSubLevelConsumptionData(List<String> companyName, String meterTypeId) {
        // 获取下一层级的消耗数据
        List<Map<String, Object>> subLevelData = pointDao.getSubLevelConsumptionData(companyName, meterTypeId);
        // 如果没有数据，直接返回空列表
        if (subLevelData.isEmpty()) {
            log.error("没有下一层级的数据");
            return Collections.emptyList();
        }
        // 计算总消耗量
        double totalConsumption = subLevelData.stream()
                .mapToDouble(data -> ((Number) data.get("total_consumption")).doubleValue())
                .sum();

        // 转换为百分比形式
        List<Map<String, Object>> result = new ArrayList<>();
        for (Map<String, Object> data : subLevelData) {
            Map<String, Object> newData = new HashMap<>(data);

            double consumption = ((Number) data.get("total_consumption")).doubleValue();
            double percentage = totalConsumption > 0 ? (consumption / totalConsumption) * 100 : 0;

            newData.put("percentage", Math.round(percentage * 100) / 100 + "%"); // 保留两位小数
            newData.put("total_consumption", consumption); // 保持原始消耗值

            result.add(newData);
        }

        return result;
    }



}